<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="../css/main.css" media="all" rel="stylesheet" type="text/css" />
<link href="../css/highlight.css" media="all" rel="stylesheet" type="text/css" />
<link href="../css/print.css" media="print" rel="stylesheet" type="text/css" />
<title>第6章 - コントローラーレイヤーの内側</title>
</head>

<body>
<div class="navigation">

<table width="100%">
<tr>
<td width="40%" align="left"><a href="05-Configuring-Symfony.html">前の章</a></td>
<td width="20%" align="center"><a href="index.html">ホーム</a></td>
<td width="40%" align="right"><a href="07-Inside-the-View-Layer.html">次の章</a></td>
</tr>
</table>
<hr/>
</div>

<div>
<a name="chapter.6.inside.the.controller.layer" id="chapter.6.inside.the.controller.layer"></a><h1>第6章 - コントローラーレイヤーの内側</h1>

<p>symfonyにおいて、コントローラーレイヤーはビジネスロジックとプレゼンテーションを結びつけるコードを格納し、異なる目的で利用するためにいくつかのコンポーネントに分割されます:</p>

<ul>
<li>フロントコントローラー(front controller)はアプリケーションへの唯一のエントリーポイント(entry point - 入り口)です。設定をロードし、実行するアクションを決定します。</li>
<li>アクション(action)はアプリケーションのロジックを格納します。リクエストの整合性をチェックし、プレゼンテーションレイヤーが必要なデータを準備します。</li>
<li>リクエスト、レスポンス、セッションオブジェクトはリクエストパラメーター、レスポンスヘッダー、永続的なユーザーデータにアクセスできます。これらはコントローラーレイヤー内部でよく使われます。</li>
<li>フィルター(filter)は、アクションの前あとで、すべてのリクエストに対して実行されるコードの一部です。たとえば、セキュリティとバリデーション(検証)フィルターはWebアプリケーションで共通に使われます。独自フィルターを作成することでフレームワークを拡張できます。</li>
</ul>

<p>この章では、これらすべてのコンポーネントを説明しますが、数の多さに怖がらないでください。基本的なページに対して必要なことはアクションクラスのなかで数行のコードを書くことだけです。ほかのコントローラーコンポーネントは特定の状況のみに使われます。</p>

<div class="toc">
<dl>
<dt><a href="#the.front.controller">6.1. フロントコントローラー</a></dt>
<dd><dl>
<dt><a href="#the.front.controllers.job.in.detail">6.1.1. フロントコントローラーの仕事の詳細</a></dt>
<dt><a href="#the.default.front.controller">6.1.2. デフォルトのフロントコントローラー</a></dt>
<dt><a href="#calling.another.front.controller.to.switch.the.environment">6.1.3. 環境を切り替えるためにほかのフロントコントローラーを呼び出す</a></dt>
</dl></dd>
<dt><a href="#actions">6.2. アクション</a></dt>
<dd><dl>
<dt><a href="#the.action.class">6.2.1. アクションクラス</a></dt>
<dt><a href="#alternative.action.class.syntax">6.2.2. アクションクラスの代替構文</a></dt>
<dt><a href="#retrieving.information.in.the.action">6.2.3. アクションの情報をとり出す</a></dt>
<dt><a href="#action.termination">6.2.4. アクションの終了方法</a></dt>
<dt><a href="#skipping.to.another.action">6.2.5. 別のアクションにスキップする</a></dt>
<dt><a href="#repeating.code.for.several.actions.of.a.module">6.2.6. 1つのモジュールの複数のアクションのあいだでコードを繰り返す</a></dt>
</dl></dd>
<dt><a href="#accessing.the.request">6.3. リクエストにアクセスする</a></dt>
<dt><a href="#user.session">6.4. ユーザーセッション</a></dt>
<dd><dl>
<dt><a href="#accessing.the.user.session">6.4.1. ユーザーセッションにアクセスする</a></dt>
<dt><a href="#flash.attributes">6.4.2. flash属性</a></dt>
<dt><a href="#session.management">6.4.3. セッションの管理</a></dt>
</dl></dd>
<dt><a href="#action.security">6.5. アクションのセキュリティ</a></dt>
<dd><dl>
<dt><a href="#access.restriction">6.5.1. アクセスの制限</a></dt>
<dt><a href="#granting.access">6.5.2. アクセス権を付与する</a></dt>
<dt><a href="#complex.credentials">6.5.3. 複雑なクレデンシャル</a></dt>
</dl></dd>
<dt><a href="#validation.and.errorhandling.methods">6.6. バリデーションとエラー処理のメソッド</a></dt>
<dt><a href="#filters">6.7. フィルター</a></dt>
<dd><dl>
<dt><a href="#the.filter.chain">6.7.1. フィルターチェーン</a></dt>
<dt><a href="#building.your.own.filter">6.7.2. 独自フィルターを開発する</a></dt>
<dt><a href="#filter.activation.and.parameters">6.7.3. フィルターの有効化とパラメーター</a></dt>
<dt><a href="#sample.filters">6.7.4. サンプルのフィルター</a></dt>
</dl></dd>
<dt><a href="#module.configuration">6.8. モジュールの設定</a></dt>
<dt><a href="#summary">6.9. まとめ</a></dt>
</dl>
</div>
<a name="the.front.controller" id="the.front.controller"></a><h2>フロントコントローラー</h2>

<p>すべてのWebリクエストは1つのフロントコントローラーによって処理されます。フロントコントローラーは特定の環境におけるアプリケーション全体への唯一のエントリーポイントです。</p>

<p>フロントコントローラーはリクエストを受けとるとき、ユーザーが入力した(もしくはクリックした)URLを用いてアクションとモジュールの名前をマッチさせるルーティングシステムを使います。たとえば、つぎのリクエストURLは<code>index.php</code>スクリプト(フロントコントローラー)を呼び出し、<code>mymodule</code>モジュールの<code>myAction</code>アクションの呼び出しとして解釈されます:</p>

<pre><code>http://localhost/index.php/mymodule/myAction
</code></pre>

<p>symfonyの内部にご興味がなければ、フロントコントローラーについて知る必要のあることはこれだけです。これはsymfonyのMVCアーキテクチャの不可欠なコンポーネントですが、変更が必要になることはほとんどありません。フロントコントローラーの内部構造を本当に理解したいと思わなければ、つぎのセクションに飛ぶことができます。</p>

<a name="the.front.controllers.job.in.detail" id="the.front.controllers.job.in.detail"></a><h3>フロントコントローラーの仕事の詳細</h3>

<p>フロントコントローラーはリクエストのディスパッチ(dispatch - 発送)を行いますが、このことは単に実行するアクションを決定することよりも少し多くのことが行われていることを意味します。実際、つぎのようなすべてのアクションに共通なコードを実行します:</p>

<ol>
<li>プロジェクトの設定クラスとsymfonyのライブラリをロードする</li>
<li>アプリケーションの設定とsymfonyの内容を作成する</li>
<li>symfonyのコアクラスをロードして初期化する</li>
<li>設定をロードする</li>
<li>実行するアクションとリクエストパラメーターを決定するためにリクエストURLをデコードする</li>
<li>アクションが存在しない場合、404エラーのアクションにリダイレクトする</li>
<li>フィルターを有効にする(たとえば、リクエストが認証を必要とする場合)</li>
<li>フィルターを実行する(ファーストパス部分)*</li>
<li>アクションを実行しビューをレンダリングする</li>
<li>フィルターを実行する(セカンドパス部分)*</li>
<li>レスポンスを出力する</li>
</ol>

<p>(訳注)* は、図6-3およびリスト6-30を参照</p>

<a name="the.default.front.controller" id="the.default.front.controller"></a><h3>デフォルトのフロントコントローラー</h3>

<p>デフォルトのフロントコントローラーは、<code>index.php</code>という名前でプロジェクトの<code>web/</code>ディレクトリに設置されています。これはリスト6-1で示されるシンプルなPHPファイルです。</p>

<p>リスト6-1 - 運用環境用のデフォルトのフロントコントローラー</p>

<pre class="php"><span class="kw2">&lt;?php</span>
&nbsp;
<span class="kw1">require_once</span><span class="br0">&#40;</span><span class="kw3">dirname</span><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/../config/ProjectConfiguration.class.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$configuration</span> <span class="sy0">=</span> ProjectConfiguration<span class="sy0">::</span><span class="me2">getApplicationConfiguration</span><span class="br0">&#40;</span><span class="st_h">'frontend'</span><span class="sy0">,</span> <span class="st_h">'prod'</span><span class="sy0">,</span> <span class="kw4">false</span><span class="br0">&#41;</span><span class="sy0">;</span>
sfContext<span class="sy0">::</span><span class="me2">createInstance</span><span class="br0">&#40;</span><span class="re0">$configuration</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">dispatch</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>フロントコントローラーはアプリケーションの設定をインクルードします。これはステップ2から4に該当します。<code>sfController</code>オブジェクトの<code>dispatch()</code>メソッドの呼び出し(symfonyのMVCアーキテクチャのコアコントローラー)はリクエストをディスパッチします。これはステップ5から7に該当します。最後のステップはフィルターチェーンによって処理されます。これはこの章のあとで説明します。</p>

<a name="calling.another.front.controller.to.switch.the.environment" id="calling.another.front.controller.to.switch.the.environment"></a><h3>環境を切り替えるためにほかのフロントコントローラーを呼び出す</h3>

<p>環境ごとに1つのフロントコントローラーが存在します。当然のことながら、これは環境を定義するフロントコントローラーそのものです。環境は<code>ProjectConfiguration::getApplicationConfiguration()</code>メソッドの呼び出しに渡す2番目の引数によって定義されます。</p>

<p>ブラウザーでアプリケーションを見ながら環境を変更するには、ほかのフロントコントローラーを選びます。<code>generate:app</code>タスクで新しいアプリケーションを作成するときに利用できるデフォルトのフロントコントローラーは運用環境用の<code>index.php</code>と開発環境用の<code>frontend_dev.php</code>です(アプリケーションの名前が<code>frontend</code>であることが前提)。URLがフロントコントローラーのスクリプト名を含まないとき、デフォルトの<code>mod_rewrite</code>設定は<code>index.php</code>を使います。両方のURLは運用環境で同じページ(<code>mymodule/index</code>)を表します:</p>

<pre><code>http://localhost/index.php/mymodule/index
http://localhost/mymodule/index
</code></pre>

<p>そしてこのURLは開発環境で同じページを表示します:</p>

<pre><code>http://localhost/frontend_dev.php/mymodule/index
</code></pre>

<p>新しい環境を作成することは新しいフロントコントローラーを作成することと同じぐらい簡単です。たとえば、運用環境に移行するまえに顧客がアプリケーションをテストできるようにステージング環境(staging environment)が必要になることがあります。このステージング環境を作成するには、<code>web/frontend_dev.php</code>の内容を<code>web/frontend_staging.php</code>にコピーして、<code>ProjectConfiguration::getApplicationConfiguration()</code>の呼び出しの2番目の引数の値を<code>staging</code>に変更します。すべての設定ファイルにおいて、リスト6-2で示されるように、この環境に対して特定の値を設定するために、新しい<code>staging:</code>セクションを追加できます。</p>

<p>リスト6-2 - ステージング環境のための特別な設定を格納する<code>app.yml</code>のサンプル</p>

<pre><code>staging:
  mail:
    webmaster:    dummy@mysite.com
    contact:      dummy@mysite.com
all:
  mail:
    webmaster:    webmaster@mysite.com
    contact:      contact@mysite.com
</code></pre>

<p>この新しい環境でアプリケーションがどのように反応するのかを見たければ、関連するフロントコントローラーを呼び出します:</p>

<pre><code>http://localhost/frontend_staging.php/mymodule/index
</code></pre>

<a name="actions" id="actions"></a><h2>アクション</h2>

<p>アクションはすべてのアプリケーションのロジックを格納するので、アプリケーションのなか心的な役割を担います。アクションはモデルを使い、ビューのための変数を定義します。symfonyのアプリケーションでWebリクエストを作成するとき、URLはアクションとリクエストパラメーターを定義します。</p>

<a name="the.action.class" id="the.action.class"></a><h3>アクションクラス</h3>

<p>アクションは<code>sfActions</code>クラスを継承する<code>moduleNameActions</code>クラスの<code>executeActionName</code>メソッドであり、モジュールによって分類されます。モジュールのアクションクラスは、モジュールの<code>actions/</code>ディレクトリの<code>actions.class.php</code>ファイルに保存されます。</p>

<p>リスト6-3は全体の<code>mymodule</code>モジュールに対して<code>index</code>アクションだけを持つ<code>actions.class.php</code>ファイルの例を示しています。</p>

<p>リスト6-3 - アクションクラスのサンプル(<code>apps/frontend/modules/mymodule/actions/actions.class.php</code>)</p>

<pre class="php"><span class="kw2">class</span> mymoduleActions <span class="kw2">extends</span> sfActions
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> executeIndex<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// ...</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<blockquote class="caution"><p>
  PHPはメソッドの名前が大文字か小文字かを区別しませんがsymfonyは区別します。アクションメソッドは小文字の<code>execute</code>で始まり、つぎに最初が大文字で始まるアクションの名前が続くことを忘れないでください。</p>
</blockquote>

<p>アクションをリクエストするには、パラメーターとしてモジュール名とアクション名を使いフロントコントローラーのスクリプトを呼び出す必要があります。デフォルトでは、この作業はスクリプトに<code>module_name</code>/<code>action_name</code>の組を追加することで行われます。このことはリスト6-4で定義されたアクションがつぎのURLで呼び出されることを意味します:</p>

<pre><code>http://localhost/index.php/mymodule/index
</code></pre>

<p>リスト6-4で示されるように、より多くのアクションを追加することは<code>sfActions</code>オブジェクトにより多くの<code>execute</code>メソッドを追加することを意味します。</p>

<p>リスト6-4 - 2つのアクションを持つアクションクラス(<code>frontend/modules/mymodule/actions/actions.class.php</code>)</p>

<pre class="php"><span class="kw2">class</span> mymoduleActions <span class="kw2">extends</span> sfActions
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> executeIndex<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// ...</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> executeList<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// ...</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<p>アクションクラスのサイズが大きくなりすぎたら、リファクタリングを行いコードをモデルレイヤーに移動させることが必要でしょう。通常は、アクションのコードを短く保ち(数行以内)、すべてのビジネスロジックをモデル内部に保つべきです。</p>

<p>それでも、1つのモジュール内部にたくさんのアクションが存在するのであれば、そのモジュールを2つに分割することが重要です。</p>

<blockquote class="sidebar"><p class="title">
  symfonyのコーディング規約</p>
  
  <p>この本で示されたコードの例において、開き波かっこと閉じ波かっこ(<code>{</code>と<code>}</code>)がそれぞれ一行を占めることにおそらくお気づきでしょう。この規約によってコードはより読みやすくなります。</p>
  
  <p>symfonyのほかのコーディング規約では、インデントはつねに2つの空白文字で行われます: タブは使いません。これはテキストエディタによってタブは異なる空白文字の値を持つので、タブと空白のインデントが混在するコードを見分けることが不可能だからです。</p>
  
  <p>symfonyコアと生成されたPHPファイルは通常の閉じタグの<code>?&gt;</code>で終わりません。これは本当に必要がないからと、このタグの後ろに空白がある場合、出力の問題が引き起こされる可能性があるからです。</p>
  
  <p>そして本当に注意を払っているのであれば、symfonyでは決して空白文字では終わりません。今回の場合、理由はつまらないことです: Fabien(筆者の一人)のテキストエディタでは空白で終わる行が不細工に見えるからです！</p>
</blockquote>

<a name="alternative.action.class.syntax" id="alternative.action.class.syntax"></a><h3>アクションクラスの代替構文</h3>

<p>アクションの代替構文は、個別のファイル、アクションごとに1つのファイルのアクションをディスパッチするために使えます。この場合、それぞれのアクションクラスは(<code>sfActions</code>の代わりに)<code>sfAction</code>を継承し、<code>actionNameAction</code>と名づけられます。実際のアクションメソッドはたんに<code>execute</code>と名づけられます。ファイルの名前はクラスの名前と同じです。このことはリスト6-4の同等の内容はリスト6-5と6-6で示される2つのファイルで書けることを意味します。</p>

<p>リスト6-5 - 単一アクションのファイル(<code>frontend/modules/mymodules/action/indexAction.class.php</code>)</p>

<pre class="php"><span class="kw2">class</span> indexAction <span class="kw2">extends</span> sfAction
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> execute<span class="br0">&#40;</span><span class="re0">$request</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// ...</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<p>リスト6-6 - 単一アクションのファイル(<code>frontend/modules/mymodules/actions/listAction.class.php</code>)</p>

<pre class="php"><span class="kw2">class</span> listAction <span class="kw2">extends</span> sfAction
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> execute<span class="br0">&#40;</span><span class="re0">$request</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// ...</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<a name="retrieving.information.in.the.action" id="retrieving.information.in.the.action"></a><h3>アクションの情報をとり出す</h3>

<p>アクションクラスはコントローラー関連の情報とsymfonyのコアオブジェクトにアクセスする方法を提供します。リスト6-7はこれらの方法の実際の例を示しています。</p>

<p>リスト6-7 - <code>sfActions</code>の共通メソッド</p>

<pre class="php"><span class="kw2">class</span> mymoduleActions <span class="kw2">extends</span> sfActions
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> executeIndex<span class="br0">&#40;</span><span class="re0">$request</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// リクエストパラメーターを読みとる</span>
    <span class="re0">$password</span>    <span class="sy0">=</span> <span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getParameter</span><span class="br0">&#40;</span><span class="st_h">'password'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="co1">// コントローラー情報をとり出す</span>
    <span class="re0">$moduleName</span>  <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getModuleName</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="re0">$actionName</span>  <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getActionName</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="co1">// symfonyのコアオブジェクトをとり出す</span>
    <span class="re0">$userSession</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getUser</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="re0">$response</span>    <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getResponse</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="re0">$controller</span>  <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getController</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="re0">$context</span>     <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getContext</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="co1">// テンプレートに情報を渡すためにアクション変数を設定する</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">setVar</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="sy0">,</span> <span class="st_h">'bar'</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">foo</span> <span class="sy0">=</span> <span class="st_h">'bar'</span><span class="sy0">;</span>            <span class="co1">// 短いバージョン</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<blockquote class="sidebar"><p class="title">
  Context Singleton</p>
  
  <p>すでにフロントコントローラー内部で<code>sfContext::createInstance()</code>の呼び出しを見ました。アクションにおいて、<code>getContext()</code>メソッドは同じSingletonを返します。これは任意のリクエストに関連するすべてのsymfonyのコアオブジェクトへの参照を保存し、各オブジェクトに対してアクセサーを提供する非常に便利なオブジェクトです:</p>
  
  <p><code>sfController</code>: コントローラーオブジェクト(<code>-&gt;getController()</code>)</p>
  
  <p><code>sfRequest:</code> リクエストオブジェクト(<code>-&gt;getRequest()</code>)</p>
  
  <p><code>sfResponse</code>: レスポンスオブジェクト(<code>-&gt;getResponse()</code>)</p>
  
  <p><code>sfUser</code>: ユーザーセッションオブジェクト(<code>-&gt;getUser()</code>)</p>
  
  <p><code>sfDatabaseConnection</code>: データベース接続(<code>-&gt;getDatabaseConnection()</code>)</p>
  
  <p><code>sfLogger</code>: loggerオブジェクト(<code>-&gt;getLogger()</code>)</p>
  
  <p><code>sfI18N</code>: 国際化オブジェクト(<code>-&gt;getI18N()</code>)</p>
  
  <p><code>sfContext::getInstance()</code>: Singletonをコードの任意の部分から呼び出すことができます。</p>
</blockquote>

<a name="action.termination" id="action.termination"></a><h3>アクションの終了方法</h3>

<p>アクションの実行結果においてさまざまなふるまいが可能です。アクションメソッドが返す値はビューをレンダリングする方法を決定します。<code>sfView</code>クラスの定数はアクションの結果の表示に使われるテンプレートを指定するために使われます。</p>

<p>呼び出すビューのデフォルトが存在する場合(もっとも共通の事例)、アクションはつぎのように終わります:</p>

<pre class="php"><span class="kw1">return</span> sfView<span class="sy0">::</span><span class="me2">SUCCESS</span><span class="sy0">;</span></pre>

<p>symfonyは<code>actionNameSuccess.php</code>という名前のテンプレートを探します。これはアクションのデフォルトのふるまいとして定義されているので、アクションメソッド側で<code>return</code>ステートメントが省略されると、symfonyは<code>actionNameSuccess.php</code>テンプレートも探します。空のアクションでも同じふるまいをします。成功したアクションの終了方法の例はリスト6-8をご覧ください。</p>

<p>リスト6-8 - <code>indexSuccess.php</code>と<code>listSuccess.php</code>テンプレートを呼び出すアクション</p>

<pre class="php"><span class="kw2">public</span> <span class="kw2">function</span> executeIndex<span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="kw1">return</span> sfView<span class="sy0">::</span><span class="me2">SUCCESS</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">public</span> <span class="kw2">function</span> executeList<span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
<span class="br0">&#125;</span></pre>

<p>呼び出すエラービューが存在する場合、アクションはつぎのように終わります:</p>

<pre class="php"><span class="kw1">return</span> sfView<span class="sy0">::</span><span class="me2">ERROR</span><span class="sy0">;</span></pre>

<p>symfonyは<code>actionNameError.php</code>という名前のテンプレートを探します。</p>

<p>カスタムビューを呼び出すには、つぎのように終わらせます:</p>

<pre class="php"><span class="kw1">return</span> <span class="st_h">'MyResult'</span><span class="sy0">;</span></pre>

<p>symfonyは<code>actionNameMyResult.php</code>によって呼び出されたテンプレートを探します。</p>

<p>呼び出すビューが存在しない場合、たとえばバッチプロセスのなかで実行されたアクションの場合、アクションはつぎのように終わります:</p>

<pre class="php"><span class="kw1">return</span> sfView<span class="sy0">::</span><span class="me2">NONE</span><span class="sy0">;</span></pre>

<p>この場合、テンプレートは実行されません。このことはビューレイヤーを完全に回避して、アクションから直接HTMLコードを出力できることを意味します。リスト6-9で示されるように、この事例のためにsymfonyは特別な<code>renderText()</code>メソッドを提供します。これは11章で検討されるAjaxインタラクションなどのアクションの非常に高い反応性を必要とするときに役立ちます。</p>

<p>リスト6-9 - <code>sfView::NONE</code>を返すことでビューを回避してレスポンスを直接出力する</p>

<pre class="php"><span class="kw2">public</span> <span class="kw2">function</span> executeIndex<span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="kw1">echo</span> <span class="st0">&quot;&lt;html&gt;&lt;body&gt;Hello, World!&lt;/body&gt;&lt;/html&gt;&quot;</span><span class="sy0">;</span>
&nbsp;
  <span class="kw1">return</span> sfView<span class="sy0">::</span><span class="me2">NONE</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="co1">// つぎの内容と同等</span>
<span class="kw2">public</span> <span class="kw2">function</span> executeIndex<span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="kw1">return</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">renderText</span><span class="br0">&#40;</span><span class="st0">&quot;&lt;html&gt;&lt;body&gt;Hello, World!&lt;/body&gt;&lt;/html&gt;&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre>

<p>いくつかの場合において、定義されたヘッダー以外のヘッダー(特に<code>X-JSON</code>ヘッダー)をともなう空のレスポンスを送信する必要があります。つぎの章で検討しますがリスト6-10で示されているように、<code>sfResponse</code>オブジェクト経由でヘッダーを定義して<code>sfView::HEADER_ONLY</code>定数を返します。</p>

<p>リスト6-10 - ビューのレンダリングを回避してヘッダーのみを送信する</p>

<pre class="php"><span class="kw2">public</span> <span class="kw2">function</span> executeRefresh<span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="re0">$output</span> <span class="sy0">=</span> <span class="st_h">'&lt;&quot;title&quot;,&quot;My basic letter&quot;],[&quot;name&quot;,&quot;Mr Brown&quot;&gt;'</span><span class="sy0">;</span>
  <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getResponse</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">setHttpHeader</span><span class="br0">&#40;</span><span class="st0">&quot;X-JSON&quot;</span><span class="sy0">,</span> <span class="st_h">'('</span><span class="sy0">.</span><span class="re0">$output</span><span class="sy0">.</span><span class="st_h">')'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
  <span class="kw1">return</span> sfView<span class="sy0">::</span><span class="me2">HEADER_ONLY</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre>

<p>アクションを特定のテンプレートによってレンダリングしなければならない場合、<code>return</code>ステートメントを無視して、代わりに<code>setTemplate()</code>メソッドを使います。</p>

<pre class="php"><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">setTemplate</span><span class="br0">&#40;</span><span class="st_h">'myCustomTemplate'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<a name="skipping.to.another.action" id="skipping.to.another.action"></a><h3>別のアクションにスキップする</h3>

<p>アクションの実行が新しいアクションの実行をリクエストすることで終わることがあります。たとえばPOSTリクエストフォームでは通常の場合、投稿を処理するアクションはデータベースを更新したあとで別のアクションにリダイレクトします。ほかの例はアクションのエイリアスです: <code>index</code>アクションはしばし一覧を表示する方法であり、実際には<code>list</code>アクションへの転送です。</p>

<p>アクションクラスは別のアクションを実行するためのメソッドを2つ提供します:</p>

<ul>
<li><p>アクションが別のアクションにフォワードする場合:</p>

<pre class="php"><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">forward</span><span class="br0">&#40;</span><span class="st_h">'otherModule'</span><span class="sy0">,</span> <span class="st_h">'index'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre></li>
<li><p>アクションがWebリダイレクトの結果になる場合:</p>

<pre class="php"><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">redirect</span><span class="br0">&#40;</span><span class="st_h">'otherModule/index'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">redirect</span><span class="br0">&#40;</span><span class="st_h">'http://www.google.com/'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre></li>
</ul>

<blockquote class="note"><p>
  アクションにおいてフォワードもしくはリダイレクトの後に設置されたコードは決して実行されません。これらの呼び出しは<code>return</code>ステートメントと同等のものとみなすことができます。アクションの実行を停止させるためにこれらは<code>sfStopException</code>を投げます; この例外はsymfonyによってあとで捕捉され、たんに無視されます。</p>
</blockquote>

<p>ときにリダイレクトもしくはフォワードを選びにくいことがあります。最良の解決法を選ぶには、フォワードはアプリケーションの内部に存在しユーザーには見えないことを覚えておいてください。ユーザーに関しては、表示されるURLはリクエストされたものと同じです。対照的に、リダイレクトはユーザーのブラウザーへのメッセージであり、それ以降の新しいリクエストとURLの最後の結果の変更を含みます。</p>

<p>アクションが<code>method="post"</code>メソッドで投稿されたフォームから呼び出された場合、つねにリダイレクトを行うべきです。主な利点はユーザーが結果ページをリフレッシュするさいにフォームが再投稿されないことです; 加えて、フォームを表示して、ユーザーがPOSTリクエストを再投稿したいかどうかを確認するアラートを表示しないことで前のページに戻るボタンが期待どおりに動作します。</p>

<p>とてもよく使われる特殊なフォワードが1つあります。<code>forward404()</code>メソッドは"見つからないページ"用のアクションにフォワードします。アクションの実行に必要なパラメーターがリクエストのなかに存在しないときに、このメソッドはよく呼び出されます(誤って入力されたURLを検出します)。リスト6-11は<code>id</code>パラメーターを必要とする<code>show</code>アクションの例を示しています。</p>

<p>リスト6-11 - <code>forward404()</code>メソッドの使いかた</p>

<pre class="php"><span class="kw2">public</span> <span class="kw2">function</span> executeShow<span class="br0">&#40;</span><span class="re0">$request</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="re0">$article</span> <span class="sy0">=</span> ArticlePeer<span class="sy0">::</span><span class="me2">retrieveByPK</span><span class="br0">&#40;</span><span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getParameter</span><span class="br0">&#40;</span><span class="st_h">'id'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="re0">$article</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">forward404</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<blockquote class="tip"><p>
  404エラー用のアクションとテンプレートは<code>$sf_symfony_ lib_dir/controller/default/</code>ディレクトリで見つかります。新しい<code>default</code>モジュールをあなたのアプリケーションに追加することで、もしくはフレームワーク内に設置されたものをオーバーライドすることで、または内部で<code>error404</code>アクションと<code>error404Success</code>テンプレートを定義することによって、このページをカスタマイズできます。代わりの方法として、既存のアクションを使うために<code>settings.yml</code>ファイルで<code>error_404_module</code>と<code>error_404_action</code>アクション定数を設定することもできます。</p>
</blockquote>

<p>経験則によれば、多くの場合リスト6-12にような何かをテストした後にアクションはリダイレクトもしくはフォワードを作ることがあります。sfActionsクラスのメソッド、<code>forwardIf()</code>、<code>forwardUndless()</code>、<code>forward404If()</code>、<code>forward404Unless()</code>、<code>redirectIf()</code>と<code>redirectUnless()</code>があるのはそういうわけです。リスト6-12で示されるように、これらのメソッドは、テストして(<code>xxxIf()</code>メソッドに対して)trueもしくは(<code>xxxUnless()メソッド</code>)falseを返された場合に実行する条件を表す、1つの追加パラメーターをとります。</p>

<p>リスト6-12 - <code>forward404If()</code>メソッドの使いかた</p>

<pre class="php"><span class="co1">// このアクションはリスト6-12で示されているものと同等である</span>
<span class="kw2">public</span> <span class="kw2">function</span> executeShow<span class="br0">&#40;</span><span class="re0">$request</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="re0">$article</span> <span class="sy0">=</span> ArticlePeer<span class="sy0">::</span><span class="me2">retrieveByPK</span><span class="br0">&#40;</span><span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getParameter</span><span class="br0">&#40;</span><span class="st_h">'id'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">forward404If</span><span class="br0">&#40;</span><span class="sy0">!</span><span class="re0">$article</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="co1">// これも同じ</span>
<span class="kw2">public</span> <span class="kw2">function</span> executeShow<span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="re0">$article</span> <span class="sy0">=</span> ArticlePeer<span class="sy0">::</span><span class="me2">retrieveByPK</span><span class="br0">&#40;</span><span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getParameter</span><span class="br0">&#40;</span><span class="st_h">'id'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">forward404Unless</span><span class="br0">&#40;</span><span class="re0">$article</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre>

<p>これらのメソッドを使うことでコードを短く保つことができるだけでなく、読みやすくなります。</p>

<blockquote class="tip"><p>
  アクションが<code>forward404()</code>メソッドもしくはその仲間のメソッドを呼び出すとき、symfonyは404エラーのレスポンスを管理する<code>sfError404Exception</code>を投げます。コントローラーにアクセスしたくないどこかの場所から404エラーのメッセージを表示することが必要な場合、同じような例外が投げられることを意味します。</p>
</blockquote>

<a name="repeating.code.for.several.actions.of.a.module" id="repeating.code.for.several.actions.of.a.module"></a><h3>1つのモジュールの複数のアクションのあいだでコードを繰り返す</h3>

<p>アクション名を<code>executeActionName()</code>(<code>sfActions</code>クラスの場合)もしくは<code>execute()</code>メソッド(<code>sfAction</code>クラスの場合)<code>execute</code>とする規約によってsymfonyがアクションメソッドを探すことが保証されます。この規約によってメソッド名が<code>execute</code>で始まらないかぎり、アクションと見なされたくない独自のほかのメソッドを追加できるようになります。</p>

<p>アクションを実際に実行するまえにそれぞれのアクションごとにおいて複数のステートメントを繰り返す必要があるとき、別の便利なメソッドがあります。これらをアクションクラスの <code>preExecute()</code>メソッドに展開できます。アクションが実行されるたびにそのあとでステートメントを繰り返す方法を推測できるでしょう: それらを<code>postExecute()</code>メソッドにラップします。これらのメソッドの構文はリスト6-13で示されています。</p>

<p>リスト6-13 - アクションクラスのなかで<code>preExecute()</code>、<code>postExecute()</code>とカスタムメソッドを使う</p>

<pre class="php"><span class="kw2">class</span> mymoduleActions <span class="kw2">extends</span> sfActions
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> preExecute<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// ここに挿入されたコードはそれぞれのアクション呼び出しの始めに実行される</span>
    <span class="sy0">...</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> executeIndex<span class="br0">&#40;</span><span class="re0">$request</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="sy0">...</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> executeList<span class="br0">&#40;</span><span class="re0">$request</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="sy0">...</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">myCustomMethod</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>  <span class="co1">// アクションクラスのメソッドがアクセス可能である</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> postExecute<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// ここに挿入されたコードはそれぞれのアクション呼び出しの終了時に実行される</span>
    <span class="sy0">...</span>
  <span class="br0">&#125;</span>
&nbsp;
  protected <span class="kw2">function</span> myCustomMethod<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// &quot;execute&quot;で始まらないかぎり、独自メソッドも追加できる</span>
    <span class="co1">// この場合、protectedもしくはprivateとして宣言するほうがよい</span>
    <span class="sy0">...</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<a name="accessing.the.request" id="accessing.the.request"></a><h2>リクエストにアクセスする</h2>

<p>アクションメソッドの最初の引数はリクエストオブジェクトであり、symfonyでは<code>sfWebRequest</code>と呼ばれます。以前の章で名前でリクエストパラメーターの値を読みとるために使う<code>getParameter('myparam')</code>メソッドに慣れました。テーブルは<code>sfWebRequest</code>オブジェクトのもっとも便利なメソッドの一覧です。</p>

<p>テーブル6-1 - <code>sfWebRequest</code>オブジェクトのメソッド</p>

<table>
<thead>
<tr>
  <th>名前</th>
  <th>機能</th>
  <th>サンプルの出力</th>
</tr>
</thead>
<tbody>
<tr>
  <td><strong>リクエスト情報</strong></td>
  <td></td>
  <td></td>
</tr>
<tr>
  <td><code>isMethod($method)</code></td>
  <td>POSTもしくはGET？</td>
  <td>trueもしくはfalse</td>
</tr>
<tr>
  <td><code>getMethod()</code></td>
  <td>リクエストメソッドの名前</td>
  <td><code>'POST'</code></td>
</tr>
<tr>
  <td><code>getHttpHeader('Server')</code></td>
  <td>任意のHTTPヘッダーの値</td>
  <td><code>'Apache/2.0.59 (Unix) DAV/2 PHP/5.1.6'</code></td>
</tr>
<tr>
  <td><code>getCookie('foo')</code></td>
  <td>名前つきのCookieの値</td>
  <td><code>'bar'</code></td>
</tr>
<tr>
  <td><code>isXmlHttpRequest()*</code></td>
  <td>Ajaxリクエストであるか？</td>
  <td><code>true</code></td>
</tr>
<tr>
  <td><code>isSecure()</code></td>
  <td>SSLリクエストであるか？</td>
  <td><code>true</code></td>
</tr>
<tr>
  <td><strong>リクエストパラメーター</strong></td>
  <td></td>
  <td></td>
</tr>
<tr>
  <td><code>hasParameter('foo')</code></td>
  <td>リクエストにパラメーターが存在するか？</td>
  <td><code>true</code></td>
</tr>
<tr>
  <td><code>getParameter('foo')</code></td>
  <td>命名されたパラメーターの値</td>
  <td><code>'bar'</code></td>
</tr>
<tr>
  <td><code>getParameterHolder()-&gt;getAll()</code></td>
  <td>すべてのリクエストパラメーターの配列</td>
  <td></td>
</tr>
<tr>
  <td>URI関連の情報</td>
  <td></td>
  <td></td>
</tr>
<tr>
  <td><code>getUri()</code></td>
  <td>完全なURI</td>
  <td><code>'http://localhost/frontend_dev.php/mymodule/myaction'</code></td>
</tr>
<tr>
  <td><code>getPathInfo()</code></td>
  <td>パス情報</td>
  <td><code>'/mymodule/myaction'</code></td>
</tr>
<tr>
  <td><code>getReferer()**</code></td>
  <td>リファラ</td>
  <td><code>'http://localhost/frontend_dev.php/'</code></td>
</tr>
<tr>
  <td><code>getHost()</code></td>
  <td>ホスト名</td>
  <td><code>'localhost'</code></td>
</tr>
<tr>
  <td><code>getScriptName()</code></td>
  <td>フロント・コントローラーのパス名</td>
  <td><code>'frontend_dev.php'</code></td>
</tr>
<tr>
  <td><strong>クライアントのブラウザー情報</strong></td>
  <td></td>
  <td></td>
</tr>
<tr>
  <td><code>getLanguages()</code></td>
  <td>受信した言語の配列</td>
  <td><code>Array( [0] =&gt; fr [1] =&gt; fr_FR [2] =&gt; en_US [3] =&gt; en )</code></td>
</tr>
<tr>
  <td><code>getCharsets()</code></td>
  <td>受信した文字集合の配列</td>
  <td><code>Array( [0] =&gt; ISO-8859-1 [1] =&gt; UTF-8 [2] =&gt; * )</code></td>
</tr>
<tr>
  <td><code>getAcceptableContentType()</code></td>
  <td>受信したContent-Typeの配列</td>
  <td><code>Array( [0] =&gt; text/xml [1] =&gt; text/html</code></td>
</tr>
</tbody>
</table>

<ul>
<li><em>Prototype, MootoolsとjQueryで動作する</em></li>
</ul>

<p>** <em>時々プロキシでブロックされる</em></p>

<p>ユーザーがファイルを添付するmultipartリクエストに対しては、リスト6-14で示されるように<code>sfWebRequest</code>オブジェクトはこれらのファイルにアクセスして移動させる方法を提供します。これらのメソッドはsymfony 1.1で廃止されます(詳細はフォームフレームワークと<code>sfValidatorFile</code>クラスを参照)。</p>

<p>リスト6-14 - <code>sfWebRequest</code>オブジェクトは添付ファイルの処理方法を理解している</p>

<pre class="php"><span class="kw2">class</span> mymoduleActions <span class="kw2">extends</span> sfActions
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> executeUpload<span class="br0">&#40;</span><span class="re0">$request</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">hasFiles</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
      <span class="kw1">foreach</span> <span class="br0">&#40;</span><span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getFileNames</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="kw1">as</span> <span class="re0">$uploadedFile</span><span class="br0">&#41;</span>
      <span class="br0">&#123;</span>
        <span class="re0">$fileName</span>  <span class="sy0">=</span> <span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getFileName</span><span class="br0">&#40;</span><span class="re0">$uploadedFile</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="re0">$fileSize</span>  <span class="sy0">=</span> <span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getFileSize</span><span class="br0">&#40;</span><span class="re0">$uploadedFile</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="re0">$fileType</span>  <span class="sy0">=</span> <span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getFileType</span><span class="br0">&#40;</span><span class="re0">$uploadedFile</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="re0">$fileError</span> <span class="sy0">=</span> <span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">hasFileError</span><span class="br0">&#40;</span><span class="re0">$uploadedFile</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="re0">$uploadDir</span> <span class="sy0">=</span> sfConfig<span class="sy0">::</span><span class="me2">get</span><span class="br0">&#40;</span><span class="st_h">'sf_upload_dir'</span><span class="br0">&#41;</span><span class="sy0">;</span>
        <span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">moveFile</span><span class="br0">&#40;</span><span class="re0">$uploadedFile</span><span class="sy0">,</span> <span class="re0">$uploadDir</span><span class="sy0">.</span><span class="st_h">'/'</span><span class="sy0">.</span><span class="re0">$fileName</span><span class="br0">&#41;</span><span class="sy0">;</span>
      <span class="br0">&#125;</span>
    <span class="br0">&#125;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<p>サーバーが<code>$_SERVER</code>もしくは<code>$_ENV</code>変数をサポートしてくれるかどうかもしくはデフォルト値もしくはサーバーの互換性問題に悩む必要はありません。<code>sfWebRequest</code>メソッドがすべてを代行してくれます。その上、これらの名前は明確なので、リクエストから情報を手に入れる方法を調べるためにPHPのドキュメントを調べる必要はもはやありません。</p>

<blockquote class="note"><p>
  上記のコードはあたかもファイルがアップロードされたように<code>$fileName</code>を使います。これは悪意のある名前を持つファイルを送信することで悪用される小さな機会が存在するので、つねにターゲットファイル名を標準化するか生成すべきです。</p>
</blockquote>

<a name="user.session" id="user.session"></a><h2>ユーザーセッション</h2>

<p>symfonyはユーザーセッションの管理を自動化してユーザーのためにリクエスト間のデータを一貫したものに保ちます。symfonyはPHP組み込みのセッションハンドリングメカニズムを利用しこれらをより柔軟に設定可能で使いやすいものにするために強化します。</p>

<a name="accessing.the.user.session" id="accessing.the.user.session"></a><h3>ユーザーセッションにアクセスする</h3>

<p>現在のユーザーセッションオブジェクトには<code>sfUser</code>クラスのインスタンスである<code>getUser()</code>メソッドを持つアクションでアクセスします。このクラスはユーザー属性の保存を可能にするパラメーターホルダーを持ちます。リスト6-15で示されるように、このデータはユーザーセッションの終了までほかのリクエストで利用可能です。ユーザー属性はさまざまなデータ型(文字列、配列と連想配列)を保存できます。ユーザーが認証されていないとしても個別のユーザーごとに設定できます。</p>

<p>リスト6-15 - <code>sfUser</code>オブジェクトは複数のリクエストにまたがって存在するカスタムユーザー属性を保持する</p>

<pre class="php"><span class="kw2">class</span> mymoduleActions <span class="kw2">extends</span> sfActions
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> executeFirstPage<span class="br0">&#40;</span><span class="re0">$request</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$nickname</span> <span class="sy0">=</span> <span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getParameter</span><span class="br0">&#40;</span><span class="st_h">'nickname'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="co1">// データをユーザーセッションに保存する</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getUser</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">setAttribute</span><span class="br0">&#40;</span><span class="st_h">'nickname'</span><span class="sy0">,</span> <span class="re0">$nickname</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> executeSecondPage<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// デフォルト値を指定してユーザーセッションからデータをとり出す</span>
    <span class="re0">$nickname</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getUser</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getAttribute</span><span class="br0">&#40;</span><span class="st_h">'nickname'</span><span class="sy0">,</span> <span class="st_h">'Anonymous Coward'</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<blockquote class="caution"><p>
  オブジェクトをユーザーセッションに保存できますがこれは非推奨です。なぜならリクエストの間にセッションオブジェクトがシリアライズされるからです。セッションのシリアライズが解除されたとき、保存されたオブジェクトのクラスはすでにロードされなければなりませんが、つねにあてはまることではないからです。加えて、Propelオブジェクトを保存する場合、膠着状態("stalled")になったオブジェクトが存在する可能性があります。</p>
</blockquote>

<p>symfonyの多くのゲッターのように、属性が定義されていないときに使われるデフォルト値を指定するために<code>getAttribute()</code>メソッドは2番目の引数を受けとります。ユーザーのために属性が定義されているかどうかを確認するには、<code>hasAttribute()</code>メソッドを使います。属性は<code>getAttributeHolder()</code>メソッドによってアクセス可能なパラメーターホルダーに保存されます。リスト6-16で示すように、これによって通常のパラメーターホルダーのメソッドでユーザー属性を簡単に一掃できるようになります。</p>

<p>リスト6-16 - ユーザーセッションからデータを削除する</p>

<pre class="php"><span class="kw2">class</span> mymoduleActions <span class="kw2">extends</span> sfActions
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> executeRemoveNickname<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getUser</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getAttributeHolder</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">remove</span><span class="br0">&#40;</span><span class="st_h">'nickname'</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> executeCleanup<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getUser</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getAttributeHolder</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">clear</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<p>リスト6-17で示されるように、現在の<code>sfUser</code>オブジェクトを保存する<code>$sf_user</code>変数を通して、ユーザーセッション属性はテンプレートのなかでもデフォルトで利用できます。</p>

<p>リスト6-17 - テンプレートはユーザーセッション属性にもアクセスできる</p>

<pre class="php">&lt;p&gt;
  Hello, <span class="kw2">&lt;?php</span> <span class="kw1">echo</span> <span class="re0">$sf_user</span><span class="sy0">-&gt;</span><span class="me1">getAttribute</span><span class="br0">&#40;</span><span class="st_h">'nickname'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
&lt;/p&gt;</pre>

<blockquote class="note"><p>
  現在のリクエストの持続時間に関する情報を保存する必要がある場合、たとえばアクション呼び出しの連鎖を通して情報を渡すために、<code>getAttribute()</code>メソッドと<code>setAttriubte()</code>メソッドを持つ<code>sfRequest</code>クラスを使うことが望ましいかもしれません。<code>sfUser</code>オブジェクトの属性だけがリクエストの間において一貫しています。</p>
</blockquote>

<a name="flash.attributes" id="flash.attributes"></a><h3>flash属性</h3>

<p>ユーザー属性に関して繰り返し起きる問題は属性が不要になったときにユーザーセッションを消去することです。たとえば、フォームを通してデータを更新したあとに確認メッセージを表示したい場合を考えます。フォームを処理するアクションがリダイレクトを行う場合、このアクションからの情報をリダイレクトアクションに渡す唯一の方法はユーザーセッションに情報を保存することです。しかしいったん確認メッセージが表示されるら、属性をクリアする必要があります; そうでなければ、有効期間が切れるまでセッションが維持されることになります。</p>

<p>flash属性は定義した後にすぐに忘れてもよい短命の属性です。これはすぐつぎのリクエストのあとで消えるので将来のユーザーセッションはクリーンな状態に保たれます。アクションのなかでは、つぎのようにflash属性を定義します:</p>

<pre class="php"><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getUser</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">setFlash</span><span class="br0">&#40;</span><span class="st_h">'notice'</span><span class="sy0">,</span> <span class="re0">$value</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>テンプレートはレンダリングされ、別のアクションに新しくリクエストするユーザーに配信されます。この2番目のアクションにおいて、flash属性の値を得るにはつぎのように書きます:</p>

<pre class="php"><span class="re0">$value</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getUser</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getFlash</span><span class="br0">&#40;</span><span class="st_h">'notice'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>それから忘れてください。この2番目のページを配信したあとで、flash属性の<code>notice</code>は消去されます。この2番目のアクションの期間にこの属性を求めない場合でも、どのみちflash属性はセッションから消えます。</p>

<p>テンプレートからflash属性にアクセスする必要がある場合、<code>$sf_user</code>オブジェクトを使います:</p>

<pre class="php"><span class="kw2">&lt;?php</span> <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$sf_user</span><span class="sy0">-&gt;</span><span class="me1">hasFlash</span><span class="br0">&#40;</span><span class="st_h">'notice'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">:</span> <span class="sy1">?&gt;</span>
  <span class="kw2">&lt;?php</span> <span class="kw1">echo</span> <span class="re0">$sf_user</span><span class="sy0">-&gt;</span><span class="me1">getFlash</span><span class="br0">&#40;</span><span class="st_h">'notice'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
<span class="kw2">&lt;?php</span> <span class="kw1">endif</span><span class="sy0">;</span> <span class="sy1">?&gt;</span></pre>

<p>もしくはつぎのように書くこともできます:</p>

<pre class="php"><span class="kw2">&lt;?php</span> <span class="kw1">echo</span> <span class="re0">$sf_user</span><span class="sy0">-&gt;</span><span class="me1">getFlash</span><span class="br0">&#40;</span><span class="st_h">'notice'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span></pre>

<p>flash属性はすぐつぎのリクエストに情報を渡すための正当な手段です。</p>

<a name="session.management" id="session.management"></a><h3>セッションの管理</h3>

<p>symfonyのセッションハンドリングによって完全にクライアントとサーバーのセッションIDの保存は開発者に対して覆い隠されます。しかしながら、セッション管理メカニズムのデフォルトのふるまいを修正することも可能です。このセクションはおもに上級ユーザー向けです。</p>

<p>クライアントサイドにおいて、セッションはCookieによって処理されます。symfonyセッションのCookieの名前は<code>symfony</code>ですが、リスト6-18で示されるように、<code>factories.yml</code>設定ファイルを編集することでこの名前を変更できます。</p>

<p>リスト6-18 - Cookieの名前を変更する(<code>apps/frontend/config/factories.yml</code>)</p>

<pre><code>all:
  storage:
    class: sfSessionStorage
    param:
      session_name: my_cookie_name
</code></pre>

<blockquote class="tip"><p>
  <code>factories.yml</code>で<code>auto_start</code>パラメーターが<code>true</code>としてセットされている場合のみ(PHPの<code>session_start()</code>関数で)セッションは始まります(デフォルトの場合)。ユーザーセッションを手動で始めたい場合、このストレージファクトリの設定を無効にします。</p>
</blockquote>

<p>symfonyのセッションハンドリング機能はPHPセッションに基づいています。Cookieの代わりにURLパラメーターによって扱うセッションのクライアントサイドの管理が必要な場合、<code>php.ini</code>の<code>use_trans_sid</code>設定を変更する必要があります。しかしながら、これは推奨されていないことをご了承ください。</p>

<pre><code>session.use_trans_sid = 1
</code></pre>

<p>サーバーサイドにおいて、symfonyはデフォルトでユーザーセッションをファイルに保存します。リスト6-19で示されるように、<code>factories.yml</code>の<code>class</code>パラメーターの値を変更することでそれらをデータベースに保存できます</p>

<p>リスト6-19 - サーバーセッションストレージ(<code>apps/frontend/config/factories.yml</code>)</p>

<pre><code>all:
  storage:
    class: sfMySQLSessionStorage
    param:
      db_table:    session              # セッションを保存するテーブルの名前
      database:    propel               # 使うデータベース接続の名前
      # オプションのパラメーター
      db_id_col:   sess_id              # セッションidを保存するカラムの名前
      db_data_col: sess_data            # セッションのデータを保存するカラムの名前
      db_time_col: sess_time            # セッションのタイムスタンプを保存するカラムの名前
</code></pre>

<p><code>database</code>設定は使うデータベース設定を定義します。この接続に対してデータベース設定(ホスト、データベース名、ユーザー、とパスワード)を決定するために、symfonyは<code>databases.yml</code>(8章を参照)を使います。</p>

<p>利用できるセッションストレージクラスは<code>sfMySQLSessionStorage</code>、<code>sfPostgreSQLSessionStorage</code>と<code>sfPDOSessionStorage</code>です; 後者が望ましいです。セッションストレージを完全に無効にするには、<code>sfNoStorage</code>クラスを使います。</p>

<p>セッションの期限切れは30分後に自動的に起きます。このデフォルトの設定は同じ<code>factories.yml</code>設定ファイルのなかでそれぞれの環境に対して修正できますが、リスト6-20で示されるように、今回は<code>user</code>ファクトリで行います。</p>

<p>リスト6-20 - セッションの有効期間を変更する(<code>apps/frontend/config/settings.yml</code>)</p>

<pre><code>all:
  user:
    class:       myUser
    param:
      timeout:   1800           # 秒単位のセッションの有効期間
</code></pre>

<p>ファクトリを詳しく学ぶには19章を参照してください。</p>

<a name="action.security" id="action.security"></a><h2>アクションのセキュリティ</h2>

<p>アクションを実行する機能は特定の権限を持つユーザーに制限されます。この目的のためにsymfonyが提供するツールによってセキュアなアプリケーションを作ることができます。このアプリケーションでは、ユーザーはいくつかの機能もしくはアプリケーションの一部にアクセスするまえに認証される必要があります。アプリケーションをセキュアにするには2つのステップが必要です: それぞれのアクションに対してセキュリティ要件を宣言し、これらのセキュアなアクションにアクセスできるようにユーザーがログインして権限を持つことです。</p>

<a name="access.restriction" id="access.restriction"></a><h3>アクセスの制限</h3>

<p>実行されるまえに、すべてのアクションは特別なフィルターに通されます。このフィルターは現在のユーザーがリクエストしたアクションにアクセスする権限を持つかどうかをチェックします。symfonyにおいて、権限は2つの部分で構成されます:</p>

<ul>
<li>セキュアなアクションはユーザーを認証することを必要とします。</li>
<li>クレデンシャル(credential)はセキュリティをグループ単位で編成できるようにする名前つきのセキュリティ権限です。</li>
</ul>

<p>アクションへのアクセスを制限するにはモジュールの<code>config/</code>ディレクトリの<code>security.yml</code>という名前のYAML設定ファイルを作り編集します。このファイルにおいて、ユーザーがそれぞれのアクションもしくは<code>all</code>アクションに対して満たさなければならないセキュリティ要件を指定できます。リスト6-21は<code>security.yml</code>のサンプルを示しています。</p>

<p>リスト6-21 - アクセス制限の設定(<code>apps/frontend/modules/mymodule/config/security.yml</code>)</p>

<pre><code>read:
  is_secure:   off       # すべてのユーザーはreadアクションをリクエストできる

update:
  is_secure:   on        # updateアクションは認証されたユーザーに対してのみ

delete:
  is_secure:   on        # adminクレデンシャルを持つ
  credentials: admin     # 認証されたユーザーのみ

all:
  is_secure:  off        # ともかくoffはデフォルト値
</code></pre>

<p>デフォルトではアクションはセキュアではありませんので、<code>security.yml</code>もしくはアクションに関する記述が存在しない場合、アクションは誰でもアクセスできます。<code>security.yml</code>が存在する場合、symfonyはリクエストされたアクションの名前を探し、存在する場合、セキュリティ要件を満たしているかチェックします。ユーザーが制限されたアクションにアクセスしようとしたときに起きることはユーザーのクレデンシャルによって異なります:</p>

<ul>
<li>ユーザーが認証され、適切なクレデンシャルを持つ場合、アクションは実行されます。</li>
<li>ユーザーが識別されなかった場合、ユーザーはデフォルトのログインアクションにリダイレクトされます。</li>
<li>ユーザーが識別されているが、適切なクレデンシャルを持たない場合、図6-1で示されるように、ユーザーはデフォルトのセキュアなアクションにリダイレクトされます。</li>
</ul>

<p>デフォルトのログインとセキュアなページはとてもシンプルなのでカスタマイズしたいことでしょう。リスト6-22で示されるように、アプリケーションの<code>settings.yml</code>でプロパティの値を変更することで、不十分な権限の場合に呼び出されるアクションを設定できます。</p>

<p>図6-1 - デフォルトのセキュアなアクションページ</p>

<p><img src="images/F0601.jpg" alt="デフォルトのセキュアなアクションページ" title="デフォルトのセキュアなアクションページ" /></p>

<p>リスト6-22 - デフォルトのセキュリティアクションを定義する(<code>apps/frontend/config/settings.yml</code>)</p>

<pre><code>all:
  .actions:
    login_module:  default
    login_action:  login

    secure_module: default
    secure_action: secure
</code></pre>

<a name="granting.access" id="granting.access"></a><h3>アクセス権を付与する</h3>

<p>制限されたアクションにアクセスするには、ユーザーは認証されるかつ/もしくは特定のクレデンシャルを持つことが必要です。<code>sfUser</code>オブジェクトのメソッドを呼び出すことでユーザーの権限を拡張できます。ユーザーの認証ステータスは<code>setAuthenticated()</code>メソッドによって設定され<code>isAuthenticated()</code>によってチェックされます。リスト6-23はユーザー認証のシンプルな例を示します。</p>

<p>リスト6-23 - ユーザーの認証ステータスを設定する</p>

<pre class="php"><span class="kw2">class</span> myAccountActions <span class="kw2">extends</span> sfActions
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> executeLogin<span class="br0">&#40;</span><span class="re0">$request</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getParameter</span><span class="br0">&#40;</span><span class="st_h">'login'</span><span class="br0">&#41;</span> <span class="sy0">==</span> <span class="st_h">'foobar'</span><span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
      <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getUser</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">setAuthenticated</span><span class="br0">&#40;</span><span class="kw4">true</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> executeLogout<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getUser</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">setAuthenticated</span><span class="br0">&#40;</span><span class="kw4">false</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<p>クレデンシャルをチェック、追加、削除、クリアできるので、クレデンシャルの取扱いは少し複雑です。リスト6-24は<code>sfUser</code>クラスのクレデンシャルメソッドを説明しています。</p>

<p>リスト6-24 - アクションのなかでユーザーのクレデンシャルを処理する</p>

<pre class="php"><span class="kw2">class</span> myAccountActions <span class="kw2">extends</span> sfActions
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> executeDoThingsWithCredentials<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$user</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getUser</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="co1">// 1つもしくは複数のクレデンシャルを追加する</span>
    <span class="re0">$user</span><span class="sy0">-&gt;</span><span class="me1">addCredential</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="re0">$user</span><span class="sy0">-&gt;</span><span class="me1">addCredentials</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="sy0">,</span> <span class="st_h">'bar'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="co1">// ユーザーがクレデンシャルを持つかどうかを確認する</span>
    <span class="kw1">echo</span> <span class="re0">$user</span><span class="sy0">-&gt;</span><span class="me1">hasCredential</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="br0">&#41;</span><span class="sy0">;</span>                      <span class="sy0">=&gt;</span>   <span class="kw4">true</span>
&nbsp;
    <span class="co1">// ユーザーが1つのクレデンシャルを持つのか確認する</span>
    <span class="kw1">echo</span> <span class="re0">$user</span><span class="sy0">-&gt;</span><span class="me1">hasCredential</span><span class="br0">&#40;</span><span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="sy0">,</span> <span class="st_h">'bar'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>        <span class="sy0">=&gt;</span>   <span class="kw4">true</span>
&nbsp;
    <span class="co1">// ユーザーが両方のクレデンシャルを持つのか確認する</span>
    <span class="kw1">echo</span> <span class="re0">$user</span><span class="sy0">-&gt;</span><span class="me1">hasCredential</span><span class="br0">&#40;</span><span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="sy0">,</span> <span class="st_h">'bar'</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw4">false</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="sy0">=&gt;</span>   <span class="kw4">true</span>
&nbsp;
    <span class="co1">// 1つのクレデンシャルを削除する</span>
    <span class="re0">$user</span><span class="sy0">-&gt;</span><span class="me1">removeCredential</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw1">echo</span> <span class="re0">$user</span><span class="sy0">-&gt;</span><span class="me1">hasCredential</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="br0">&#41;</span><span class="sy0">;</span>                      <span class="sy0">=&gt;</span>   <span class="kw4">false</span>
&nbsp;
    <span class="co1">// すべてのクレデンシャルをクリアする(ログアウト処理で便利)</span>
    <span class="re0">$user</span><span class="sy0">-&gt;</span><span class="me1">clearCredentials</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw1">echo</span> <span class="re0">$user</span><span class="sy0">-&gt;</span><span class="me1">hasCredential</span><span class="br0">&#40;</span><span class="st_h">'bar'</span><span class="br0">&#41;</span><span class="sy0">;</span>                      <span class="sy0">=&gt;</span>   <span class="kw4">false</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<p>ユーザーが<code>foo</code>クレデンシャルを持つ場合、そのクレデンシャルを必要とする<code>security.yml</code>に対してそのユーザーはアクセスできるようになります。リスト6-25で示されるように、クレデンシャルはテンプレート内の認証された内容を表示するためだけにも使えます。</p>

<p>リスト6-25 - テンプレートのなかでユーザーのクレデンシャルを処理する</p>

<pre class="php">&lt;ul&gt;
  &lt;li&gt;<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> link_to<span class="br0">&#40;</span><span class="st_h">'section1'</span><span class="sy0">,</span> <span class="st_h">'content/section1'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>&lt;/li&gt;
  &lt;li&gt;<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> link_to<span class="br0">&#40;</span><span class="st_h">'section2'</span><span class="sy0">,</span> <span class="st_h">'content/section2'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>&lt;/li&gt;
  <span class="kw2">&lt;?php</span> <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$sf_user</span><span class="sy0">-&gt;</span><span class="me1">hasCredential</span><span class="br0">&#40;</span><span class="st_h">'section3'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">:</span> <span class="sy1">?&gt;</span>
  &lt;li&gt;<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> link_to<span class="br0">&#40;</span><span class="st_h">'section3'</span><span class="sy0">,</span> <span class="st_h">'content/section3'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>&lt;/li&gt;
  <span class="kw2">&lt;?php</span> <span class="kw1">endif</span><span class="sy0">;</span> <span class="sy1">?&gt;</span>
&lt;/ul&gt;</pre>

<p>認証ステータスに関しては、ログイン処理の間にしばしばユーザーにクレデンシャルが付与されます。中央管理方式でユーザーのセキュリティステータスを設定するために、しばし<code>sfUser</code>オブジェクトにログインとログアウトのメソッドが追加され拡張される理由はそういうことです。</p>

<blockquote class="tip"><p>
  symfonyのプラグインである、<code>sfGuardPlugin</code><a href="http://www.symfony-project.org/plugins/sfGuardPlugin">(http://www.symfony-project.org/plugins/sfGuardPlugin)</a>はログインとログアウトを簡単にするセッションクラスを拡張します。詳細な情報は17章を参照してください。</p>
</blockquote>

<a name="complex.credentials" id="complex.credentials"></a><h3>複雑なクレデンシャル</h3>

<p>security.ymlファイルのなかで使われるYAMLファイルは、AND型とOR型の関係を利用することで、クレデンシャルの組み合わせを持つユーザーの権限を制限できます。このような組み合わせによって、複雑なワークフローとユーザーの権限管理システム、たとえばCMS(Content Management System)を開発できます。CMSにおいて<code>admin</code>クレデンシャルを持つユーザーのみがバックオフィスにアクセス可能で、記事の作成は<code>editor</code>クレデンシャルを持つユーザーだけが、記事の公開は<code>publisher</code>クレデンシャルを持つユーザーだけが可能です。リスト6-26はこの例を示しています。</p>

<p>リスト6-26 - クレデンシャル構文の組み合わせ</p>

<pre><code>editArticle:
  credentials: [ admin, editor ]              # admin AND editor

publishArticle:
  credentials: [ admin, publisher ]           # admin AND publisher

userManagement:
  credentials: [[ admin, superuser ]]         # admin OR superuser
</code></pre>

<p>新しいレベルの角かっこを追加するたびにロジックのANDとORをお互いに交換できます。つぎのように、とても複雑なスクレデンシャルの組み合わせを作ることができます:</p>

<pre><code>credentials: [[root, [supplier, [owner, quasiowner]], accounts]]
             # root OR (supplier AND (owner OR quasiowner)) OR accounts
</code></pre>

<a name="validation.and.errorhandling.methods" id="validation.and.errorhandling.methods"></a><h2>バリデーションとエラー処理のメソッド</h2>

<blockquote class="note"><p>
  このセクションで説明されている機能はsymfony 1.1で非推奨で<code>sfCompat10</code>プラグインを有効にした場合のみ動作します。</p>
</blockquote>

<p>アクションの入力バリデーションは、たいていはリクエストパラメーターですが、繰り返しが多い単調なタスクです。symfonyは、アクションクラスのメソッドを利用する、組み込みのリクエストのバリデーションシステムを提供します。</p>

<p>具体例から始めましょう。ユーザーが<code>myAction</code>にリクエストするとき、symfonyはつねに最初に<code>validateMyAction</code>という名前のメソッドを探します。見つかる場合、symfonyはそのメソッドを実行します。このバリデーションメソッドの戻り値はつぎに実行するメソッドを決定します: <code>true</code>を返す場合、<code>executeMyAction()</code>が実行されます; そうでなければ、<code>handleErrorMyAction()</code>が実行されます。そして、後者の場合、<code>handlerErrorMyAction()</code>は存在しないので、symfonyは一般的な<code>handleError()</code>メソッドを探します。両方とも存在しない場合、<code>myActionError.php</code>テンプレートをレンダリングする<code>sfView::ERROR</code>を単に返すだけです。図6-2はこのプロセスを描写しています</p>

<p>図6-2 - バリデーションのプロセス</p>

<p><img src="images/F0602.png" alt="バリデーションのプロセス" title="バリデーションのプロセス" /></p>

<p>ですのでバリデーションの秘訣はアクションメソッドのために命名規約を尊重することです:</p>

<ul>
<li><code>validateActionName</code>はバリデーションメソッドで、<code>true</code>もしくは<code>false</code>を返します。<code>ActionName</code>アクションがリクエストされたときに最初に探されるメソッドです。存在しない場合、アクションメソッドは直接実行されます。</li>
<li><code>handleErrorActionName</code>はバリデーションメソッドが失敗したときに呼び出されるメソッドです。存在しない場合、<code>Error</code>テンプレートが表示されます。</li>
<li><code>executeActionName</code>はアクションメソッドです。すべてのアクションに対して存在しなければなりません。</li>
</ul>

<p>リスト6-27はバリデーションメソッドによるアクションクラスの例を示します。この例において、バリデーションが成功するか失敗するかにかかわらず、<code>myActionSuccess.php</code>テンプレートは実行されますが、同じパラメーターでは実行されません。</p>

<p>リスト6-27 - バリデーションメソッドのサンプル</p>

<pre class="php"><span class="kw2">class</span> mymoduleActions <span class="kw2">extends</span> sfActions
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> validateMyAction<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">return</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getRequestParameter</span><span class="br0">&#40;</span><span class="st_h">'id'</span><span class="br0">&#41;</span> <span class="sy0">&gt;</span> <span class="nu0">0</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> handleErrorMyAction<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">message</span> <span class="sy0">=</span> <span class="st0">&quot;無効なパラメーター&quot;</span><span class="sy0">;</span>
&nbsp;
    <span class="kw1">return</span> sfView<span class="sy0">::</span><span class="me2">SUCCESS</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> executeMyAction<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">message</span> <span class="sy0">=</span> <span class="st0">&quot;パラメーターは正しい&quot;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<p><code>validate()</code>メソッドに望むコードを追加できます。これらのメソッドが<code>true</code>もしくは<code>false</code>のどちらかを返すものであることを確認してください。これは<code>sfActions</code>クラスのメソッドなので<code>sfRequest</code>と<code>sfUser</code>オブジェクトにも同じようにアクセスできます。これは入力とコンテキストのバリデーションのために非常に役立ちます。</p>

<p>フォームのバリデーションを実装するためにこのメカニズムを利用できますが(つまり、処理するまえにユーザーが入力した値をコントロールする)、これは繰り返されるタイプのタスクなので、10章で説明されるように、このためにsymfonyは自動化ツールを提供します。</p>

<a name="filters" id="filters"></a><h2>フィルター</h2>

<p>セキュリティ処理はすべてのリクエストがアクションを実行するまえに通過しなければならない1つのフィルター(filter)として理解できます。フィルターのなかで実行されるいくつかのテストにしたがって、リクエストの処理は、たとえば実行されるアクションを変更することで修正されます(セキュリティフィルターの場合、リクエストされたアクションの代わりにdefault/secure)。symfonyはこのアイディアをフィルタークラスに発展させます。アクションの実行前、もしくはレスポンスのレンダリングのまえに実行されるフィルタークラスの数を指定し、すべてのリクエストに対してこれを行います。フィルターをコードにまとめる方法としてみなすことができます。これは<code>preExecute()</code>と<code>postExecute()</code>と似ていますが、より高いレベルです(モジュール全体の代わりにアプリケーション全体)。</p>

<a name="the.filter.chain" id="the.filter.chain"></a><h3>フィルターチェーン</h3>

<p>実際にはsymfonyはリクエストの処理をフィルターチェーン(filter chain)と見なします。リクエストがフレームワークによって受信されたとき、最初のフィルター(つねに<code>sfRenderingFilter</code>)が実行されます。ある時点で、チェーンのなかのつぎのフィルターを呼び出し、同じように続きます。最後のフィルター(つねに<code>sfExecutionFilter</code>)が実行されるとき、以前のフィルターを終了させることが可能で、フィルターのレンダリングなどに戻ります。図6-3は、連続するダイアグラムで、模型の小さなフィルターチェーン(本物はもっと多くのフィルターを含む)を使ってこのアイディアを説明しています。</p>

<p>図6-3 - フィルターチェーンのサンプル</p>

<p><img src="images/F0603.png" alt="図6-3 - フィルターチェーンのサンプル" title="図6-3 - フィルターチェーンのサンプル" /></p>

<p>このプロセスはフィルタークラスの構造が正しいことを納得させてくれます。これらすべてのフィルタークラスは<code>sfFilter</code>クラスを継承し、<code>$filterChain</code>オブジェクトをパラメーターとして必要とする<code>execute()</code>メソッドを持ちます。このメソッドのどこかで、フィルターは<code>$filterChain-&gt;execute()</code>を呼び出すことでチェーンのつぎのフィルターに移動します。リスト6-28を例としてご覧ください。基本的には、フィルターは2つの部分に分割されます:</p>

<ul>
<li><code>$filterChain-&gt;execute()</code>の呼び出し前のコードはアクションが実行されるまえに実行されます。</li>
<li><code>$filterChain-&gt;execute()</code>の呼び出し後のコードはアクションが実行された後とレンダリングのまえに実行されます。</li>
</ul>

<p>リスト6-28 - フィルタークラスの構造</p>

<pre class="php"><span class="kw2">class</span> myFilter <span class="kw2">extends</span> sfFilter
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> execute <span class="br0">&#40;</span><span class="re0">$filterChain</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// この部分のコードは、アクションが実行されるまえに実行される(訳注：ファーストパス部分)</span>
    <span class="sy0">...</span>
&nbsp;
    <span class="co1">// チェーンでつぎのフィルターを実行する</span>
    <span class="re0">$filterChain</span><span class="sy0">-&gt;</span><span class="me1">execute</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="co1">// この部分のコードは、アクションが実行された後、レンダリングが実行されるまえに実行される(訳注：セカンドパス部分)</span>
    <span class="sy0">...</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<p>リスト6-29で示されるように、デフォルトのフィルターチェーンは<code>filters.yml</code>という名前のアプリケーション設定ファイルで定義されます。このファイルはすべてのリクエストに対して実行される予定のフィルターの一覧を表示します。</p>

<p>リスト6-29 - デフォルトのフィルターチェーン(<code>frontend/config/filters.yml</code>)</p>

<pre><code>rendering: ~
security:  ~

# 一般的に、ここで独自フィルターを差し込む

cache:     ~
common:    ~
execution: ~
</code></pre>

<p>これらの宣言はパラメーターを持ちません(チルダ文字<code>~</code>はYAMLにおいて"null"を意味します)。なぜなら、symfonyはコアで定義されたパラメーターを継承するからです。コアにおいて、symfonyはそれぞれのフィルターに対して<code>class</code>と<code>param</code>設定を定義します。たとえば、リスト6-30は<code>rendering</code>フィルター用のデフォルトパラメーターを示しています。</p>

<p>リスト6-30 - フィルターをレンダリングするためのデフォルトパラメーター(<code>$sf_symfony_data_dir/config/filters.yml</code>)</p>

<pre><code>rendering:
  class: sfRenderingFilter   # フィルタークラス
  param:                     # フィルターパラメーター
    type: rendering
</code></pre>

<p>空の値(<code>~</code>)をアプリケーションの<code>filters.yml</code>に残すことで、コア内部で定義されたデフォルト設定をフィルターに適用するようにsymfonyに伝えます。</p>

<p>さまざまな方法でフィルターチェーンをカスタマイズできます:</p>

<ul>
<li><p><code>enabled: off</code>パラメーターを追加することでチェーン内の複数のフィルターを無効にできます。たとえば、CSSとJavaScriptをheadの部分に挿入する<code>common</code>フィルターを無効にするには、つぎのように書きます:</p>

<pre><code>common:
  enabled: off
</code></pre></li>
<li><p>フィルターを無効にするには<code>filters.yml</code>からエントリーを削除しないでください； この場合、symfonyは例外を投じます。</p></li>
<li>カスタムフィルターを追加するには独自の宣言をチェーンのどこか(通常は<code>security</code>フィルターの後)に追加してください(つぎのセクションで検討)。<code>rendering</code>フィルターは最初のエントリーでなければならないこと、<code>execution</code>フィルターはフィルターチェーンの最後のエントリーでなければならないことに注意してください。</li>
<li>デフォルトフィルターのデフォルトクラスとパラメーターをオーバーライドします(特にセキュリティシステムを修正して、独自のセキュリティフィルターを使うため)。</li>
</ul>

<a name="building.your.own.filter" id="building.your.own.filter"></a><h3>独自フィルターを開発する</h3>

<p>フィルターの開発はとてもシンプルです。オートロード機能を利用するには、リスト6-31のような定義を作りプロジェクトの<code>lib/</code>フォルダーの1つに設置します。</p>

<p>アクションは別のアクションにフォワードするかリダイレクトすることが可能で、結果としてフィルターのフルチェーンを再起動するので、独自フィルターの実行をリクエストの最初のアクション呼び出しに制限したいことがあります。この目的のために<code>sfFilter</code>クラスの<code>isFirstCall()</code>メソッドはブール値を返します。この呼び出しの意味があるのはアクションが実行されるまえだけです。</p>

<p>これらの概念は実例でよりあきらかになります。リスト6-31は、ログインアクションによって作られたことを前提とした、名前が<code>MyWebSite</code>である固有のCookieを持つユーザーを自動ログインするために使われるフィルターを示します。ログインフォームに提供された"remember me"の機能を実装することは初歩的ですが実用的な方法です。</p>

<p>リスト6-31 - フィルタークラスのサンプル(<code>apps/frontend/lib/rememberFilter.class.php</code>)</p>

<pre class="php"><span class="kw2">class</span> rememberFilter <span class="kw2">extends</span> sfFilter
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> execute<span class="br0">&#40;</span><span class="re0">$filterChain</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// このフィルターを1回だけ実行する</span>
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">isFirstCall</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
      <span class="co1">// フィルターはリクエストとユーザーのオブジェクトに直接アクセスできない。</span>
      <span class="co1">// これらを手に入れるためにcontextオブジェクトを使う必要がある</span>
      <span class="re0">$request</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getContext</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getRequest</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
      <span class="re0">$user</span>    <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getContext</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getUser</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
      <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getCookie</span><span class="br0">&#40;</span><span class="st_h">'MyWebSite'</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
      <span class="br0">&#123;</span>
        <span class="co1">// ログイン</span>
        <span class="re0">$user</span><span class="sy0">-&gt;</span><span class="me1">setAuthenticated</span><span class="br0">&#40;</span><span class="kw4">true</span><span class="br0">&#41;</span><span class="sy0">;</span>
      <span class="br0">&#125;</span>
    <span class="br0">&#125;</span>
&nbsp;
    <span class="co1">// つぎのフィルターを実行する</span>
    <span class="re0">$filterChain</span><span class="sy0">-&gt;</span><span class="me1">execute</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<p>いくつかの場合において、フィルターチェーンを実行する代わりに、フィルターの最後で特定のアクションにフォワードすることが必要になります。<code>sfFilter</code>は<code>forward()</code>メソッドを持ちませんが<code>sfContoroller</code>が代行するので、つぎのコードを呼び出すことで簡単に実現できます:</p>

<pre class="php"><span class="kw1">return</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getContext</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getController</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">forward</span><span class="br0">&#40;</span><span class="st_h">'mymodule'</span><span class="sy0">,</span> <span class="st_h">'myAction'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<blockquote class="note"><p>
  <code>sfFilter</code>クラスは<code>initialize()</code>メソッドを持ち、このメソッドはフィルターオブジェクトが作られたときに実行されます。独自の方法でフィルターパラメーター(次で説明されるように<code>filters.yml</code>ファイルで定義される)を処理する必要がある場合、カスタムフィルター内でそのメソッドをオーバーライドできます。</p>
</blockquote>

<a name="filter.activation.and.parameters" id="filter.activation.and.parameters"></a><h3>フィルターの有効化とパラメーター</h3>

<p>フィルターファイルを有効にするには作成するだけでは不十分です。フィルターをフィルターチェーンに追加する必要があります。そのためには、リスト6-32で示されるように、アプリケーションもしくはモジュールの<code>config/</code>ディレクトリに設置される、<code>filteres.yml</code>のなかでフィルタークラスを宣言しなければなりません。</p>

<p>リスト6-32 - フィルターのサンプルを有効にするファイル(<code>apps/frontend/config/filters.yml</code>)</p>

<pre><code>rendering: ~
security:  ~

remember:                 # フィルターは独自の名前が必要
  class: rememberFilter
  param:
    cookie_name: MyWebSite
    condition:   %APP_ENABLE_REMEMBER_ME%

cache:     ~
common:    ~
execution: ~
</code></pre>

<p>有効にされたとき、フィルターはそれぞれのリクエストに対して実行されます。フィルターの設定ファイルは<code>param</code>キーのもとで1つもしくは複数のパラメーターの定義を持ちます。フィルタークラスは<code>getParameter()</code>メソッドを使ってこれらのパラメーターの値を得る機能を持ちます。リスト6-33ではフィルターパラメーターの値を得る方法を示しています。</p>

<p>リスト6-33 - パラメーターの値を得る(<code>apps/frontend/lib/rememberFilter.class.php</code>)</p>

<pre class="php"><span class="kw2">class</span> rememberFilter <span class="kw2">extends</span> sfFilter
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> execute <span class="br0">&#40;</span><span class="re0">$filterChain</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// ...</span>
&nbsp;
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getCookie</span><span class="br0">&#40;</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getParameter</span><span class="br0">&#40;</span><span class="st_h">'cookie_name'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
      <span class="co1">// ...</span>
    <span class="br0">&#125;</span>
&nbsp;
    <span class="co1">// ...</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<p>フィルターを実行しなければならないのかを確かめるために<code>condition</code>パラメーターはフィルターチェーンによってテストされます。リスト6-32のように、フィルターの宣言はアプリケーションの設定に依存する可能性があります。rememberフィルターはアプリケーションの<code>app.yml</code>がつぎの内容を示す場合のみ実行されます:</p>

<pre><code>all:
  enable_remember_me: on
</code></pre>

<a name="sample.filters" id="sample.filters"></a><h3>サンプルのフィルター</h3>

<p>フィルター機能はすべてのアクションに対してコードを繰り返すために便利です。たとえば、外部の分析システムを利用する場合、おそらくは外部のトラッカースクリプトを呼び出すコードスニペットをすべてのページに設置することが必要です。このコードをグローバルレイアウトに設置できますが、すべてのアプリケーションに対して有効になってしまいます。代わりの方法として、リスト6-34で示されるように、コードをフィルターのなかで設置してモジュール単位で有効にできます。</p>

<p>リスト6-34 - Google Analyticsのフィルター</p>

<pre class="php"><span class="kw2">class</span> sfGoogleAnalyticsFilter <span class="kw2">extends</span> sfFilter
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> execute<span class="br0">&#40;</span><span class="re0">$filterChain</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// アクションのまえは何もしない</span>
    <span class="re0">$filterChain</span><span class="sy0">-&gt;</span><span class="me1">execute</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="co1">// トラッカーコードでレスポンスをデコレートする</span>
    <span class="re0">$googleCode</span> <span class="sy0">=</span> <span class="st_h">'
&lt;script src=&quot;http://www.google-analytics.com/urchin.js&quot;  type=&quot;text/javascript&quot;&gt;
&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
  _uacct=&quot;UA-'</span><span class="sy0">.</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getParameter</span><span class="br0">&#40;</span><span class="st_h">'google_id'</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'&quot;;urchinTracker();
&lt;/script&gt;'</span><span class="sy0">;</span>
    <span class="re0">$response</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getContext</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">getResponse</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="re0">$response</span><span class="sy0">-&gt;</span><span class="me1">setContent</span><span class="br0">&#40;</span><span class="kw3">str_ireplace</span><span class="br0">&#40;</span><span class="st_h">'&lt;/body&gt;'</span><span class="sy0">,</span> <span class="re0">$googleCode</span><span class="sy0">.</span><span class="st_h">'&lt;/body&gt;'</span><span class="sy0">,</span><span class="re0">$response</span><span class="sy0">-&gt;</span><span class="me1">getContent</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
   <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<p>フィルターはHTMLではないレスポンスにトラッカーを追加しないので、フィルターは完全なものではないことに注意してください。</p>

<p>別の例は、リクエストをSSLに切り替えるフィルターです。リスト6-35で示されるように、このフィルターはコミュニケーションを安全にするために、まだSSLに切り替えられていない場合に切り替えが行われます。</p>

<p>リスト6-35 - セキュアなコミュニケーションフィルター</p>

<pre class="php"><span class="kw2">class</span> sfSecureFilter <span class="kw2">extends</span> sfFilter
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> execute<span class="br0">&#40;</span><span class="re0">$filterChain</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$context</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">getContext</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="re0">$request</span> <span class="sy0">=</span> <span class="re0">$context</span><span class="sy0">-&gt;</span><span class="me1">getRequest</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
    <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy0">!</span><span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">isSecure</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
      <span class="re0">$secure_url</span> <span class="sy0">=</span> <span class="kw3">str_replace</span><span class="br0">&#40;</span><span class="st_h">'http'</span><span class="sy0">,</span> <span class="st_h">'https'</span><span class="sy0">,</span> <span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getUri</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
      <span class="kw1">return</span> <span class="re0">$context</span><span class="sy0">-&gt;</span><span class="me1">getController</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">redirect</span><span class="br0">&#40;</span><span class="re0">$secure_url</span><span class="br0">&#41;</span><span class="sy0">;</span>
      <span class="co1">// フィルターチェーンを継続しない</span>
    <span class="br0">&#125;</span>
    <span class="kw1">else</span>
    <span class="br0">&#123;</span>
      <span class="co1">// すでにリクエストはセキュアなので、続けることができる</span>
      <span class="re0">$filterChain</span><span class="sy0">-&gt;</span><span class="me1">execute</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<p>フィルターはアプリケーションの機能をグローバルに拡張できるので、フィルターはプラグインで広く使われます。プラグインを詳しく学ぶには17章を参照してください。より多くのフィルターの例に関して公式サイトのwiki(<a href="http://trac.symfony-project.org/wiki">http://trac.symfony-project.org/wiki</a>)をご覧ください。</p>

<a name="module.configuration" id="module.configuration"></a><h2>モジュールの設定</h2>

<p>モジュールのふるまいのいくつかは設定に依存します。修正するには、モジュールの<code>config/</code>ディレクトリに<code>module.yml</code>ファイルを作り、環境ごとに(もしくはすべての環境: <code>all:</code>ヘッダー)設定を定義しなければなりません。リスト6-36は<code>mymodule</code>モジュールのための<code>module.yml</code>の例を示します。</p>

<p>リスト6-36 - モジュール設定(<code>apps/frontend/modules/mymodule/config/module.yml</code>)</p>

<pre><code>all:                 # すべての環境用
  enabled:     true
  is_internal: false
  view_class:  sfPHP
</code></pre>

<p><code>enabled</code>パラメーターによってモジュールのすべてのアクションを無効にできます。すべてのアクションは<code>module_disabled_module/module_disabled_action</code>アクションにリダイレクトされます(<code>settings.yml</code>で定義)。</p>

<p><code>is_internal</code>パラメーターによってモジュールのすべてのアクションの実行を内部呼び出しに制限できます。この機能は、たとえば、Eメールのメッセージを、外部からではなく、内部から送るために、別のアクションから呼び出さなければならないメールアクションに対して便利です。</p>

<p><code>view_name</code>パラメーターはビュークラスを定義します。このパラメーターは<code>sfView</code>から継承しなければなりません。この値をオーバーライドすることでSmartyなどのほかのテンプレートシステムによるビューシステムを利用できるようになります。</p>

<a name="summary" id="summary"></a><h2>まとめ</h2>

<p>symfonyにおいて、コントロールレイヤーは2つの部分(フロントコントローラーとアクション)に分割されます。フロントコントローラー(front controller)は任意の環境、ページロジックを格納するアクションのためのアプリケーションへの唯一のエントリーポイント(entry point - 入り口)です。アクション(action)はページロジックを格納します。アクションは<code>sfView</code>定数の1つを返すことでビューが実行される方法を決める機能を持ちます。アクションの内部では、リクエストオブジェクト(<code>sfRequest</code>)と現在のユーザーセッションオブジェクト(<code>sfUser</code>)を含む、コンテキストの異なる要素を操作することができます。</p>

<p>セッションオブジェクト、アクションオブジェクトとセキュリティの設定の力を一体化することで、アクセス制限機能とクレデンシャル(credential)を持つ完全なセキュリティシステムが提供されます。特別な<code>validate()</code>と<code>handleError()</code>メソッドによってリクエストのバリデーション(validation)をアクションのなかで扱うことができます。そして<code>preExecute()</code>と<code>postExecute()</code>メソッドがモジュール内部のコードの再利用のために役立つ場合、フィルター(filter)は、コントローラーのコードをリクエストごとに実行することで、すべてのアプリケーションに対して同じ再利用性を公認します。</p>
</div>
<div class="navigation">
<hr/>
<table width="100%">
<tr>
<td width="40%" align="left"><a href="05-Configuring-Symfony.html">前の章</a></td>
<td width="20%" align="center"><a href="index.html">ホーム</a></td>
<td width="40%" align="right"><a href="07-Inside-the-View-Layer.html">次の章</a></td>
</tr>
</table>

</div>
</body>

</html>
